#pragma once

#include "log.hpp"
#include "Socket.hpp"
#include "epoller.hpp"
#include "nocopy.hpp"

#include <iostream>
#include <sys/epoll.h>
#include <memory>

static const uint16_t defaultport = 8082;
static const int num = 64;

uint32_t EVENT_IN = EPOLLIN;
uint32_t EVENT_OUT = EPOLLOUT;

class EpollServer : Nocopy
{
public:
    EpollServer(const uint16_t port = defaultport)
        : _listen_sockfd_ptr(new Sock()), _epoller_ptr(new Epoller()), _port(port)
    {
    }

    ~EpollServer()
    {
        _listen_sockfd_ptr->Close();
    }

public:
    void InitServer()
    {
        _listen_sockfd_ptr->Socket();
        _listen_sockfd_ptr->Bind(_port);
        _listen_sockfd_ptr->Listen();

        LOG(Info, "server init success");
    }
    void StartServer()
    {
        // 将listen_sockfd和要监听的事件--添加到epoll模型的红黑树中
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, _listen_sockfd_ptr->Fd(), EVENT_IN);

        struct epoll_event revs[num];
        for (;;)
        {
            int n = _epoller_ptr->EpollerWait(revs, num);
            if (n > 0) // 有事件就绪了
            {
                LOG(Info, "event happened");
                std::cout << "event happened count: " << n << std::endl;
                HandlerEvent(revs, n);
            }
            else if (n == 0) // 超时了
            {
                LOG(Info, "time out...");
            }
            else // 出错了
            {
                LOG(Fatal, "epoll wait error");
                break;
            }
        }
    }
    void Accepter()
    {
        // 获取新连接
        uint16_t clientport;
        std::string clientip;

        int sockfd = _listen_sockfd_ptr->Accept(&clientip, &clientport);
        if (sockfd > 0)
        {
            // 把建立的连接要处理的事件放入epoll中
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sockfd, EVENT_IN);
        }
    }
    void Recver(int fd)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer)-1);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "get a message#" << buffer << std::endl;

            write(fd, buffer, sizeof(buffer));
        }
        else if(n == 0)
        {
            LOG(Waring, "client fd close");
            //要先把fd事件从红黑树中移除，在关闭文件描述符，因为epoll操作时，要对一个合法的文件描述符操作
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        else
        {
            LOG(Waring, "read error");
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
    }
    void HandlerEvent(struct epoll_event revs[], int num)
    {
        for (int i = 0; i < num; i++)
        {
            uint32_t event = revs[i].events;
            int fd = revs[i].data.fd;
            if (event & EVENT_IN)
            {
                if (fd == _listen_sockfd_ptr->Fd())
                {
                    Accepter();   //链接管理器
                }
                else
                {
                    std::cout << "写入"  << fd << std::endl;
                    Recver(fd);// 读取数据
                }
            }
            else if (event & EVENT_OUT)
            {
            }
            else
            {
            }
        }
    }

private:
    std::shared_ptr<Sock> _listen_sockfd_ptr;
    std::shared_ptr<Epoller> _epoller_ptr;
    uint16_t _port;
};